package com.fsy.its.netty.gbt.decoder;

import com.fsy.its.netty.msg.*;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 国标协议控制单元解码
 * 输出：应用单元数据
 * +------+----------------------------
 * | 2字节 | 业务流水号
 * | 2字节 | 协议版本号
 * | 6字节 | 时间标签
 * | 6字节 | 源地址
 * | 6字节 | 目的地址
 * | 2字节 | 应用数据单元长度
 * | 1字节 | 命令字
 * | n字节 | 应用数据单元（最大1024字节）
 * +------+----------------------------
 *
 * 应用数据单元格式
 * +=======+============================
 * | 1字节  | 类型标识
 * | 1字节  | 信息对象数目
 * |       | 信息对象1，根据类型不同长度不同
 * |       | ......
 * |       | 信息对象n
 * +=======+============================
 */
@Slf4j
@Component
@Scope("prototype")
public class GbtMessageDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf buffer, List<Object> list) throws Exception {
        MsgHead msgHead = new MsgHead();

        //业务流水号
        msgHead.setSequence(buffer.readUnsignedShortLE());

        //协议版本号
        msgHead.setVersion(buffer.readUnsignedShortLE());

        //时间标签
        msgHead.setTime(decodeTime(buffer));

        //源地址和目的地址
        byte[] srcAddr = new byte[6];
        byte[] dstAddr = new byte[6];
        buffer.readBytes(srcAddr);
        buffer.readBytes(dstAddr);
        msgHead.setSrcAddr(ByteBufUtil.hexDump(srcAddr));
        msgHead.setDstAddr(ByteBufUtil.hexDump(dstAddr));

        //应用数据单元长度
        int appDataLen = buffer.readShortLE();
        //log.info("App data length: {}", appDataLen);

        //命令字
        short cmd = buffer.readUnsignedByte();
        msgHead.setCommand(cmd);
        //log.info("App command: {}", cmd);

        /**
         * 应用数据单元
         */
        if( appDataLen > 0 ) {
            //应用数据类型标识
            short appDataType = buffer.readUnsignedByte();
            //信息对象个数
            short infoObjNum = buffer.readUnsignedByte();
            for (int i = 0; i < infoObjNum; i++) {
                BaseMsg msg = null;
                switch(appDataType) {
                    case 1: //建筑消防设施系统状态
                        msg = decodeFireSys(buffer);
                        break;
                    case 2: //建筑消防设施部件运行状态
                        msg = decodeFireComponent(buffer);
                        break;
                    //TODO: 解析更多的信息对象
                }
                if( msg != null ) {
                    msg.head = msgHead;
                    list.add(msg);
                }
                else {
                    log.error("Unknown app data type: {}", appDataType);
                }
            }
        }
        //忽略掉剩下多余的数据
        buffer.skipBytes(buffer.readableBytes());
    }

    /**
     * 解析建筑消防设施系统状态
     * 1字节  系统类型标识
     * 1字节  系统地址
     * 2字节  系统状态
     * @param buffer
     * @return
     */
    private FireStateMsg decodeFireSys(ByteBuf buffer) {
        FireStateMsg msg = new FireStateMsg();
        msg.setSys(new FireObjectInfo());
        msg.getSys().setType(buffer.readByte());
        msg.getSys().setAddr(ByteBufUtil.hexDump(new byte[]{buffer.readByte()}));
        readFireStates(buffer, msg);
        return msg;
    }

    /**
     * 解析建筑消防设施部件状态
     * 1字节  系统类型标识
     * 1字节  系统地址
     * 1字节  部件类型
     * 4字节  部件地址
     * 2字节  部件状态
     * 31字节 部件说明
     * @return
     */
    private FireStateMsg decodeFireComponent(ByteBuf buffer) throws UnsupportedEncodingException {
        FireStateMsg msg = new FireStateMsg();

        msg.setSys(new FireObjectInfo());
        msg.getSys().setType(buffer.readByte());
        msg.getSys().setAddr(ByteBufUtil.hexDump(new byte[]{buffer.readByte()}));

        msg.setComponent(new FireObjectInfo());
        msg.getComponent().setType(buffer.readByte());
        msg.getComponent().setAddr(Integer.toHexString(buffer.readInt()));
        readFireStates(buffer, msg);
        byte[] desc = new byte[31];
        buffer.readBytes(desc);
        msg.getComponent().setDesc(new String(desc, "gbk"));

        return msg;
    }

    private QingdaoHeartMsg decodeQingdaoHeartMsg(ByteBuf buffer) {
        QingdaoHeartMsg msg = new QingdaoHeartMsg();
        msg.data = new byte[buffer.readableBytes()];
        buffer.readBytes(msg.data);
        return msg;
    }

    private FireStateMsg readFireStates(ByteBuf buffer, FireStateMsg msg) {
        int states = buffer.readUnsignedShort();
        msg.setAlarm(((1<<1)&states)>0 ? (byte)1:0);
        msg.setFault(((1<<2)&states)>0 ? (byte)1:0);
        msg.setShield(((1<<3)&states)>0 ? (byte)1:0);
        msg.setSupervise(((1<<4)&states)>0 ? (byte)1:0);
        msg.setStart(((1<<5)&states)>0 ? (byte)1:0);
        msg.setFeedback(((1<<6)&states)>0 ? (byte)1:0);
        msg.setDelay(((1<<7)&states)>0 ? (byte)1:0);
        msg.setMainPowerFault(((1<<8)&states)>0 ? (byte)1:0);
        msg.setSecondPowerFault(((1<<9)&states)>0 ? (byte)1:0);
        msg.setBusFault(((1<<10)&states)>0 ? (byte)1:0);
        msg.setManual(((1<<11)&states)>0 ? (byte)1:0);
        msg.setConfigChanged(((1<<12)&states)>0 ? (byte)1:0);
        msg.setReset(((1<<13)&states)>0 ? (byte)1:0);
        return msg;
    }

    private LocalDateTime decodeTime(ByteBuf buffer) {
        short second = buffer.readUnsignedByte();
        short minute = buffer.readUnsignedByte();
        short hour = buffer.readUnsignedByte();
        short day = buffer.readUnsignedByte();
        short month = buffer.readUnsignedByte();
        int year = 2000+ buffer.readUnsignedByte();
        return LocalDateTime.of(year, month, day, hour, minute, second);
    }
}
